package extended

import (
	"jvm/instructions/base"
	"jvm/instructions/loads"
	"jvm/instructions/math"
	"jvm/instructions/stores"
	"jvm/rtda"
	. "jvm/typedef"
)

// *****************************************************************
// 栈顶弹出引用，若引用为null，跳转
type IFNULL struct {
	base.BranchInst
}

func (self *IFNULL) Execute(frame *rtda.Frame) {
	ref := frame.OPStack().PopRef()
	if ref == nil {
		self.SetBranchPC(frame)
	}

}

// *****************************************************************
type IFNONNULL struct {
	base.BranchInst
}

func (self *IFNONNULL) Execute(frame *rtda.Frame) {
	ref := frame.OPStack().PopRef()
	if ref != nil {
		self.SetBranchPC(frame)
	}

}

// *****************************************************************
// 无条件跳转，offset为四字节
type GOTO_W struct {
	offset I4
}

func (self *GOTO_W) ReadOperands(reader *base.CodeReader) {
	self.offset = reader.ReadI4()
}

func (self *GOTO_W) Execute(frame *rtda.Frame) {
	branch(frame, self.offset)
}

// *****************************************************************
// 跳转通用函数
func branch(frame *rtda.Frame, offset I4) {
	pc := I8(frame.Thread().PC())
	Offset := I8(offset)
	addr := pc + Offset
	frame.SetNextPC(U4(addr))
}

// *****************************************************************
type WIDE struct {
	wideInst base.Inst
}

func (self *WIDE) ReadOperands(reader *base.CodeReader) {
	opcode := reader.ReadU1()
	switch opcode {
	case 0x15:
		inst := &loads.ILOAD{}
		inst.Index = reader.ReadU2()
		self.wideInst = inst
	case 0x16:
		inst := &loads.LLOAD{}
		inst.Index = reader.ReadU2()
		self.wideInst = inst
	case 0x17:
		inst := &loads.FLOAD{}
		inst.Index = reader.ReadU2()
		self.wideInst = inst
	case 0x18:
		inst := &loads.DLOAD{}
		inst.Index = reader.ReadU2()
		self.wideInst = inst
	case 0x19:
		inst := &loads.ALOAD{}
		inst.Index = reader.ReadU2()
		self.wideInst = inst
	case 0x36:
		inst := &stores.ISTORE{}
		inst.Index = reader.ReadU2()
		self.wideInst = inst
	case 0x37:
		inst := &stores.LSTORE{}
		inst.Index = reader.ReadU2()
		self.wideInst = inst
	case 0x38:
		inst := &stores.FSTORE{}
		inst.Index = reader.ReadU2()
		self.wideInst = inst
	case 0x39:
		inst := &stores.DSTORE{}
		inst.Index = reader.ReadU2()
		self.wideInst = inst
	case 0x3a:
		inst := &stores.ASTORE{}
		inst.Index = reader.ReadU2()
		self.wideInst = inst
	case 0x84:
		inst := &math.IINC{}
		inst.Index = reader.ReadU2()
		inst.Val = I4(reader.ReadI2())
		self.wideInst = inst
	case 0xa9: // ret
		panic("Unsupported opcode: 0xa9!")
	}
}

func (self *WIDE) Execute(frame *rtda.Frame) {
	self.wideInst.Execute(frame)
}
